﻿using System;
using System.Collections.Generic;
using System.Linq;
//using System.Core.dll 
//using System.Data.dll 
//using System.Data.DataSetExtensions.dll;
//using System.Xml.dll;
using System.Data;
using System.Data.SqlClient;

namespace CodeGeneratorT4
{
    public class DbHelper
    {
        #region GetDbTables

        public static List<DbTable> GetDbTables(string connectionString, string tables = null)
        {

            if (!string.IsNullOrEmpty(tables))
            {
                tables = string.Format(" and obj.name in ('{0}')", tables.Replace(",", "','"));
            }

            #region SQL 

            string sql = @"SELECT
									obj.name tablename,
									schem.name schemname,
									idx.rows,
									CAST
									(
										CASE 
											WHEN (SELECT COUNT(1) FROM sys.indexes WHERE object_id= obj.OBJECT_ID AND is_primary_key=1) >=1 THEN 1
											ELSE 0
										END 
									AS BIT) HasPrimaryKey   ,ext.value Remark                                        
									from  sys.objects obj 
									INNER JOIN  dbo.sysindexes idx on obj.object_id=idx.id and idx.indid<=1
									INNER JOIN  sys.schemas schem ON obj.schema_id=schem.schema_id
									LEFT JOIN sys.extended_properties ext on  obj.object_id=ext.major_id and ext.minor_id<1 
									where type='U' AND obj.name !='sysdiagrams'  " + tables + " 	order by obj.name";

            #endregion

            DataTable dt = GetDataTable(connectionString, sql);
            return dt.Rows.Cast<DataRow>().Select(row => new DbTable
            {
                TableName = row.Field<string>("tablename"),
                SchemaName = row.Field<string>("schemname"),
                Rows = row.Field<int>("rows"),
                HasPrimaryKey = row.Field<bool>("HasPrimaryKey"),
                Remark =row.Field<string>("Remark") ?? row.Field<string>("tablename"),
            }).ToList();
        }


        #endregion

        #region GetDbColumns

        public static List<DbColumn> GetDbColumns(string connectionString, string tableName, string schema = "dbo")
        {
            #region SQL

            string sql = @"
                                    WITH indexCTE AS
                                    (
	                                    SELECT 
                                        ic.column_id,
                                        ic.index_column_id,
                                        ic.object_id    
                                        FROM  sys.indexes idx
                                        INNER JOIN sys.index_columns ic ON idx.index_id = ic.index_id AND idx.object_id = ic.object_id
                                        WHERE  idx.object_id =OBJECT_ID(@tableName) AND idx.is_primary_key=1
                                    )
                                    select
									colm.column_id ColumnId,
                                    CAST(CASE WHEN indexCTE.column_id IS NULL THEN 0 ELSE 1 END AS BIT) IsPrimaryKey,
                                    colm.name ColumnName,
                                    systype.name ColumnType,
                                    colm.is_identity IsIdentity,
                                    colm.is_nullable IsNullable,
                                    cast(colm.max_length as int) ByteLength,
                                    (
                                        case 
                                            when systype.name='nvarchar' and colm.max_length>0 then colm.max_length/2 
                                            when systype.name='nchar' and colm.max_length>0 then colm.max_length/2
                                            when systype.name='ntext' and colm.max_length>0 then colm.max_length/2 
                                            else colm.max_length
                                        end
                                    ) CharLength,
                                    cast(colm.precision as int) Precision,
                                    cast(colm.scale as int) Scale,
                                    prop.value Remark
                                    from sys.columns colm
                                    inner join sys.types systype on colm.system_type_id=systype.system_type_id and colm.user_type_id=systype.user_type_id
                                    left join sys.extended_properties prop on colm.object_id=prop.major_id and colm.column_id=prop.minor_id
                                    LEFT JOIN indexCTE ON colm.column_id=indexCTE.column_id AND colm.object_id=indexCTE.object_id                                        
                                    where colm.object_id=OBJECT_ID(@tableName)
                                    order by colm.column_id";

            #endregion

            SqlParameter param = new SqlParameter("@tableName", SqlDbType.NVarChar, 100)
            {
                Value = $"{schema}.{tableName}"
            };
            DataTable dt = GetDataTable(connectionString, sql, param);
            return dt.Rows.Cast<DataRow>().Select(row => new DbColumn()
            {
                ColumnId = row.Field<int>("ColumnId"),
                IsPrimaryKey = row.Field<bool>("IsPrimaryKey"),
                ColumnName = row.Field<string>("ColumnName"),
                ColumnType = row.Field<string>("ColumnType"),
                IsIdentity = row.Field<bool>("IsIdentity"),
                IsNullable = row.Field<bool>("IsNullable"),
                ByteLength = row.Field<int>("ByteLength"),
                CharLength = row.Field<int>("CharLength"),
                Scale = row.Field<int>("Scale"),
                Remark = row["Remark"].ToString().Equals("") ? row["ColumnName"].ToString() : row["Remark"].ToString()
            }).ToList();
        }

        #endregion

        #region GetDataTable

        public static DataTable GetDataTable(string connectionString, string commandText, params SqlParameter[] parms)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                SqlCommand command = connection.CreateCommand();
                command.CommandText = commandText;
                command.Parameters.AddRange(parms);
                SqlDataAdapter adapter = new SqlDataAdapter(command);

                DataTable dt = new DataTable();
                adapter.Fill(dt);

                return dt;
            }
        }

        #endregion

        #region 得到主键

        //得到主键
        public static string GetPrimaryKey(List<DbColumn> dbColumns)
        {
            string primaryKey = string.Empty;
            if (dbColumns != null && dbColumns.Count > 0)
            {
                foreach (var item in dbColumns)
                {
                    if (item.IsPrimaryKey)
                    {
                        primaryKey = item.ColumnName;
                    }
                }
            }
            return primaryKey;
        }

        //得到主键类型
        public static string GetPrimaryKeyType(List<DbColumn> dbColumns)
        {
            string primaryKey = string.Empty;
            if (dbColumns != null && dbColumns.Count > 0)
            {
                foreach (var item in dbColumns)
                {
                    if (item.IsPrimaryKey)
                    {
                        primaryKey = item.CSharpType;
                    }
                }
            }
            return primaryKey;
        }

        #endregion
    }

    #region DbTable

    /// <summary>
    /// 表结构
    /// </summary>
    public sealed class DbTable
    {
        /// <summary>
        /// 表名称
        /// </summary>
        public string TableName { get; set; }

        /// <summary>
        /// 表的架构
        /// </summary>
        public string SchemaName { get; set; }

        /// <summary>
        /// 表的记录数
        /// </summary>
        public int Rows { get; set; }

        /// <summary>
        /// 是否含有主键
        /// </summary>
        public bool HasPrimaryKey { get; set; }

        /// <summary>
        /// 描述
        /// </summary>
        public string Remark { get; set; }
    }

    #endregion

    #region DbColumn

    /// <summary>
    /// 表字段结构
    /// </summary>
    public sealed class DbColumn
    {
        /// <summary>
        /// 字段ID
        /// </summary>
        public int ColumnId { get; set; }

        /// <summary>
        /// 是否主键
        /// </summary>
        public bool IsPrimaryKey { get; set; }

        /// <summary>
        /// 字段名称
        /// </summary>
        public string ColumnName { get; set; }

        /// <summary>
        /// 字段类型
        /// </summary>
        public string ColumnType { get; set; }

        /// <summary>
        /// 数据库类型对应的C#类型
        /// </summary>
        public string CSharpType
        {
            get { return SqlServerDbTypeMap.MapCsharpType(ColumnType); }
        }

        /// <summary>
        /// 
        /// </summary>
        public Type CommonType
        {
            get { return SqlServerDbTypeMap.MapCommonType(ColumnType); }
        }

        /// <summary>
        /// 字节长度
        /// </summary>
        public int ByteLength { get; set; }

        /// <summary>
        /// 字符长度
        /// </summary>
        public int CharLength { get; set; }

        /// <summary>
        /// 小数位
        /// </summary>
        public int Scale { get; set; }

        /// <summary>
        /// 是否自增列
        /// </summary>
        public bool IsIdentity { get; set; }

        /// <summary>
        /// 是否允许空
        /// </summary>
        public bool IsNullable { get; set; }

        /// <summary>
        /// 描述
        /// </summary>
        public string Remark { get; set; }
    }

    #endregion

    #region SqlServerDbTypeMap

    public class SqlServerDbTypeMap
    {
        public static string MapCsharpType(string dbtype)
        {
            if (string.IsNullOrEmpty(dbtype)) return dbtype;
            dbtype = dbtype.ToLower();
            string csharpType;
            switch (dbtype)
            {
                case "bigint":
                    csharpType = "long";
                    break;
                case "binary":
                    csharpType = "byte[]";
                    break;
                case "bit":
                    csharpType = "bool";
                    break;
                case "char":
                    csharpType = "string";
                    break;
                case "date":
                    csharpType = "DateTime";
                    break;
                case "datetime":
                    csharpType = "DateTime";
                    break;
                case "datetime2":
                    csharpType = "DateTime";
                    break;
                case "datetimeoffset":
                    csharpType = "DateTimeOffset";
                    break;
                case "decimal":
                    csharpType = "decimal";
                    break;
                case "float":
                    csharpType = "double";
                    break;
                case "image":
                    csharpType = "byte[]";
                    break;
                case "int":
                    csharpType = "int";
                    break;
                case "money":
                    csharpType = "decimal";
                    break;
                case "nchar":
                    csharpType = "string";
                    break;
                case "ntext":
                    csharpType = "string";
                    break;
                case "numeric":
                    csharpType = "decimal";
                    break;
                case "nvarchar":
                    csharpType = "string";
                    break;
                case "real":
                    csharpType = "Single";
                    break;
                case "smalldatetime":
                    csharpType = "DateTime";
                    break;
                case "smallint":
                    csharpType = "short";
                    break;
                case "smallmoney":
                    csharpType = "decimal";
                    break;
                case "sql_variant":
                    csharpType = "object";
                    break;
                case "sysname":
                    csharpType = "object";
                    break;
                case "text":
                    csharpType = "string";
                    break;
                case "time":
                    csharpType = "TimeSpan";
                    break;
                case "timestamp":
                    csharpType = "byte[]";
                    break;
                case "tinyint":
                    csharpType = "byte";
                    break;
                case "uniqueidentifier":
                    csharpType = "string";
                    break;
                case "varbinary":
                    csharpType = "byte[]";
                    break;
                case "varchar":
                    csharpType = "string";
                    break;
                case "xml":
                    csharpType = "string";
                    break;
                default:
                    csharpType = "object";
                    break;
            }
            return csharpType;
        }

        public static Type MapCommonType(string dbtype)
        {
            if (string.IsNullOrEmpty(dbtype)) return Type.Missing.GetType();
            dbtype = dbtype.ToLower();
            Type commonType;
            switch (dbtype)
            {
                case "bigint":
                    commonType = typeof (long);
                    break;
                case "binary":
                    commonType = typeof (byte[]);
                    break;
                case "bit":
                    commonType = typeof (bool);
                    break;
                case "char":
                    commonType = typeof (string);
                    break;
                case "date":
                    commonType = typeof (DateTime);
                    break;
                case "datetime":
                    commonType = typeof (DateTime);
                    break;
                case "datetime2":
                    commonType = typeof (DateTime);
                    break;
                case "datetimeoffset":
                    commonType = typeof (DateTimeOffset);
                    break;
                case "decimal":
                    commonType = typeof (decimal);
                    break;
                case "float":
                    commonType = typeof (double);
                    break;
                case "image":
                    commonType = typeof (byte[]);
                    break;
                case "int":
                    commonType = typeof (int);
                    break;
                case "money":
                    commonType = typeof (decimal);
                    break;
                case "nchar":
                    commonType = typeof (string);
                    break;
                case "ntext":
                    commonType = typeof (string);
                    break;
                case "numeric":
                    commonType = typeof (decimal);
                    break;
                case "nvarchar":
                    commonType = typeof (string);
                    break;
                case "real":
                    commonType = typeof (Single);
                    break;
                case "smalldatetime":
                    commonType = typeof (DateTime);
                    break;
                case "smallint":
                    commonType = typeof (short);
                    break;
                case "smallmoney":
                    commonType = typeof (decimal);
                    break;
                case "sql_variant":
                    commonType = typeof (object);
                    break;
                case "sysname":
                    commonType = typeof (object);
                    break;
                case "text":
                    commonType = typeof (string);
                    break;
                case "time":
                    commonType = typeof (TimeSpan);
                    break;
                case "timestamp":
                    commonType = typeof (byte[]);
                    break;
                case "tinyint":
                    commonType = typeof (byte);
                    break;
                case "uniqueidentifier":
                    commonType = typeof (string);
                    break;
                case "varbinary":
                    commonType = typeof (byte[]);
                    break;
                case "varchar":
                    commonType = typeof (string);
                    break;
                case "xml":
                    commonType = typeof (string);
                    break;
                default:
                    commonType = typeof (object);
                    break;
            }
            return commonType;
        }
    }

    #endregion

    //public class Config
    //{
    //    //数据库 连接字符串
    //    public static readonly string ConnectionString = "Server=.;Initial Catalog=YZIT;User ID=sa;Password=cqjizhi;";
    //    //生成代码的 项目名
    //    public static readonly string ProjectName = "JiZhi";
    //    //生成代码的 命名空间
    //    public static readonly string NameSpace = "Bas";
    //    //要生成的表
    //    // public static readonly string TableName = "Bas_Station";

    //    public static readonly string TableName = "Bas_Car_Ver";


    //}
}
